package org.papervision3d.core.proto
{
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.cameras.Camera3D;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import adobe.utils.CustomActions;
	import org.papervision3d.core.geom.Vertex3D;
	import org.papervision3d.core.geom.Mesh3D;
	import org.papervision3d.core.geom.Face3D;
	import org.papervision3d.core.geom.DrawTriangle;
	import org.papervision3d.events.MouseEventManager;
	import flash.geom.Rectangle;
	import flash.geom.Point;
	import org.papervision3d.core.Number3D;
	
	public class MouseEventCallback extends FindNearestCallback
	{
		
		protected var previousMatch : DisplayObject3D;
		protected var previousPath : Array;
		
		public var scene : SceneObject3D;
		public var camera : CameraObject3D;
		
		public var coordAtPoint : Vertex3D;
		public var normalAtPoint : Number3D;
		public var materialCoordinates : Point;
		
		public var match : Boolean;
		
		private var _activate : Boolean;
		
		public function set activate( flag : Boolean)  : void {
			if( flag ) {
				scene.container.addEventListener( MouseEvent.MOUSE_OVER , startLoop );
				scene.container.addEventListener( MouseEvent.MOUSE_OUT , stopLoop );
				scene.container.addEventListener( MouseEvent.MOUSE_DOWN , dispatchAction );
				scene.container.addEventListener( MouseEvent.DOUBLE_CLICK , dispatchAction );
				scene.container.addEventListener( MouseEvent.CLICK , dispatchAction );
				scene.container.addEventListener( MouseEvent.MOUSE_UP , dispatchAction );
				startLoop();
			}
			else {
				scene.container.removeEventListener( MouseEvent.MOUSE_OVER , startLoop );
				scene.container.removeEventListener( MouseEvent.MOUSE_OUT , stopLoop );
				scene.container.removeEventListener( MouseEvent.MOUSE_DOWN , dispatchAction );
				scene.container.removeEventListener( MouseEvent.DOUBLE_CLICK , dispatchAction );
				scene.container.removeEventListener( MouseEvent.CLICK , dispatchAction );
				scene.container.removeEventListener( MouseEvent.MOUSE_UP , dispatchAction );
				
				stopLoop();
			}
			_activate = flag;
		}
		
		public function set forceCapture( flag : Boolean)  : void {
			if( !_activate ) return;
			if( flag ) {
				scene.container.removeEventListener( MouseEvent.MOUSE_OVER , startLoop );
				scene.container.removeEventListener( MouseEvent.MOUSE_OUT , stopLoop );
				startLoop();
			}
			else {
				scene.container.addEventListener( MouseEvent.MOUSE_OVER , startLoop );
				scene.container.addEventListener( MouseEvent.MOUSE_OUT , stopLoop );
			}
		}
		
		public function get activate()  : Boolean {
			return _activate;
		}
		
		function MouseEventCallback( scene : SceneObject3D, camera : CameraObject3D ) {
			super( 0, 0, 100 );
			
			this.scene = scene;
			this.camera = camera;
			
			scene.container.doubleClickEnabled = true;
			
			activate = true;
		}
		
		private function startLoop( e : MouseEvent = null ) : void {
			scene.container.addEventListener( Event.ENTER_FRAME, _checkForMatch, false, 10 );
		}
		private function stopLoop( e : MouseEvent = null ) : void {
			trace( "container.MOUSE_OUT" );
			scene.container.removeEventListener( Event.ENTER_FRAME, _checkForMatch );
			if (previousPath) MouseEventManager.instance.dispatch( MouseEvent.MOUSE_OUT, previousPath, this );
			previousMatch = null;
			previousPath = null;
			curpath = new Array();
			object = null;
	        tri = null;
	        face3d = null;
	        path = null;
		}
		
		private function dispatchAction( e : MouseEvent ) : void {
			if( path ) MouseEventManager.instance.dispatch( e.type, path, this );
			else if( previousPath ) MouseEventManager.instance.dispatch( e.type, previousPath, this );
		}
		
		
		protected function _checkForMatch( e : Event ) : void {
			x = scene.container.mouseX;
			y = scene.container.mouseY;
			
			curpath = new Array();
			object = null;
	        tri = null;
	        face3d = null;
	        path = null;
	        
			scene.naRendering( camera, this );

	        match = ( object != null );

			if( match ){
				trace( "_checkForMatch : Match !!" );
				coordAtPoint = face3d.getCoordAtPoint( x, y );
				
				normalAtPoint = face3d.getNormal();
				materialCoordinates = tri.getMapCoordAtPoint( x, y );
				MouseEventManager.instance.dispatch( MouseEvent.MOUSE_MOVE, path, this );
			}
			
			if( previousMatch != object ) {
				
				if( path && previousPath){
					var paths : Object = getCleanPaths( previousPath, path );
					
					
					if( paths.prevPath.length > 0) MouseEventManager.instance.dispatch( MouseEvent.MOUSE_OUT, paths.prevPath, this );
					if( paths.path.length > 0)MouseEventManager.instance.dispatch( MouseEvent.MOUSE_OVER, paths.path, this );
				} else if( previousPath){
					MouseEventManager.instance.dispatch( MouseEvent.MOUSE_OUT, previousPath, this )
				} else {
					MouseEventManager.instance.dispatch( MouseEvent.MOUSE_OVER, path, this );
				}
			}
			
			previousPath = path;
			previousMatch = object;
			
		}
		
		
		protected function getCleanPaths( prevPath : Array, newPath : Array ) : Object {
			var ret : Object = new Object();
			var reachedIndex : int = 0;
			for( var i : int = 0; i < prevPath.length; i++ ) {
				if( prevPath[i] == newPath[i] ) reachedIndex = i;
				else break;
			}
			
			ret.prevPath = prevPath.slice( reachedIndex+1 );
			ret.path = newPath.slice( reachedIndex+1 );
			
			return ret;
		}
		
		
		
		
		
		public override function enter(node:DisplayObject3D):Boolean
        {
            if( ! node.enabled ) return false;
            return super.enter(node);
        }
        
		public override function face(face:Face3D, object:Mesh3D):void
        {
			if( ! object.enabled ) return;
        	var triangle : DrawTriangle = face.drawtriangle;
            if (distance == 0)
                if (tri != null)
                    if (triangle.screenZ > tri.screenZ)
                        return;

            if (triangle.inside(x, y))
            {
                distance = 0;
                found(triangle, face, object);
                return;
            }
            if (distance == 0) return;

            var newdist:Number = triangle.distanceToCenter(x, y);
            if (newdist < distance)
            {
                distance = newdist;
                found(triangle, face, object);
            }
        }

	}
}